package com.ruoyi.web.xxj.service.impl;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.ruoyi.common.core.domain.entity.WechatUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.web.xxj.domain.ClassOrder;
import com.ruoyi.web.xxj.domain.PaymentInfo;
import com.ruoyi.web.xxj.domain.ProductOrder;
import com.ruoyi.web.xxj.domain.RefundInfo;
import com.ruoyi.web.xxj.domain.ShopCart;
import com.ruoyi.web.xxj.domain.pojo.PrePayReq;
import com.ruoyi.web.xxj.domain.vo.ClassOrderVo;
import com.ruoyi.web.xxj.enums.Constants;
import com.ruoyi.web.xxj.service.IShopCartService;
import com.ruoyi.web.xxj.service.IWxPayService;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.web.xxj.domain.pojo.WxPayV3Bean;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
public class WxPayServiceImp implements IWxPayService {
    @Autowired
    private WxPayV3Bean wxPayConfig;
    @Autowired
    private ClassOrderServiceImpl classOrderService;
    @Autowired
    private ProductOrderServiceImpl productOrderService;
    @Autowired
    private WechatUserServiceImpl wechatUserService;
    @Autowired
    private PaymentInfoServiceImpl paymentInfoService;
    @Autowired
    private RefundInfoServiceImpl refundInfoService;
    @Autowired
    private IShopCartService shopCartService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    //只初始化一次 后台有线程会一直刷新证书
    private RSAAutoCertificateConfig config;


    @Override
    @Transactional
    public AjaxResult prePay(PrePayReq prePayVo) throws Exception {
        Long weChatUserId = SecurityUtils.getLoginUser().getWechatUser().getId();
        WechatUser wechatUser = wechatUserService.selectWechatUserById(weChatUserId);
        if (Objects.isNull(wechatUser)) {
            throw new IllegalArgumentException("微信用户不存在");
        }
        ClassOrder classOrder = classOrderService.createOrder(prePayVo, weChatUserId);
        List<ProductOrder> productOrders = productOrderService.createOrder(prePayVo, weChatUserId);
        /*if ((Objects.isNull(classOrder) && CollectionUtils.isEmpty(productOrders))
                || (!CollectionUtils.isEmpty(prePayVo.getProductOrderReqList())
        				&& prePayVo.getProductOrderReqList().size()!=(productOrders!=null?productOrders.size():0))) {
            throw new IllegalArgumentException("订单创建失败");
        }*/

        if (Objects.isNull(classOrder) && CollectionUtils.isEmpty(productOrders)) {
            throw new IllegalArgumentException("订单创建失败");
        }

        Set<String> set = new HashSet<>();
        //合并订单金额
        BigDecimal payAmount = new BigDecimal(0);
        if (Objects.nonNull(classOrder)) {
            set.add(classOrder.getOrderNo());
            BigDecimal classOrderPayAmount = classOrder.getPayAmount();
            payAmount = payAmount.add(classOrderPayAmount);
        }
        if(!CollectionUtils.isEmpty(productOrders)) {
        	for (ProductOrder productOrder : productOrders) {
        		set.add(productOrder.getOrderNo());
                BigDecimal productOrderPayAmount = productOrder.getPayAmount();
                payAmount = payAmount.add(productOrderPayAmount);
			}
        }
        //合并订单号
        String mergeOrderNo = String.join("_", set);
        //uuid作为微信的订单号 保存微信订单号和秀小吉订单号的映射关系
        String uuid = IdUtils.simpleUUID();
        redisTemplate.opsForValue().set(String.format(Constants.TransactionType.Pay.getValue(), uuid), mergeOrderNo);
        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(payAmount.multiply(new BigDecimal(100)).intValue());
        //amount.setTotal(1);
        request.setAmount(amount);
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMchId());
        request.setDescription("秀小吉订单");
        request.setNotifyUrl(wxPayConfig.getNotifyUrl());
        //request.setAttach(helperScrambleOrderIncome.getTitle());
        Payer payer = new Payer();
        payer.setOpenid(wechatUser.getOpenid());
        request.setPayer(payer);
        request.setOutTradeNo(uuid);
        request.setAttach("牛啊牛！");
        log.info("获取预支付标识: {}", JSONObject.toJSONString(request, JSONWriter.Feature.PrettyFormat));
        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response;
        try {
            response = service.prepayWithRequestPayment(request);
        } catch (ServiceException e) {
            log.error("获取预支付标识失败", e);
            throw new Exception(e.getErrorMessage());
        }
        JSONObject result = new JSONObject();
        result.put("orderId", mergeOrderNo);
        result.put("data", response);
        return AjaxResult.success(result);
    }

    /*private Integer computeAmount(ClassOrder classOrder, ProductOrder productOrder) {
        Integer classAmount = 0;
        if (Objects.nonNull(classOrder)) {
            classAmount = classOrder.getPayAmount().intValue();
        }
        Integer productAmount = 0;
        if (Objects.nonNull(productOrder)) {
            productAmount = Objects.nonNull(classOrder)?productOrder.get
        }
        return null;
    }*/


    @PostConstruct
    private void initConfig() {
        if (Objects.isNull(config)) {
            config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(wxPayConfig.getMchId())
                    .privateKeyFromPath(wxPayConfig.getKeyPath())
                    .merchantSerialNumber(wxPayConfig.getMchSerialNo())
                    .apiV3Key(wxPayConfig.getApiKey())
                    .build();
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信支付证书初始化完成");
        }
    }


    @Override
    public ResponseEntity<Void> notify(HttpServletRequest request, String body) {
        //效验签名
        String wechatpaySignature = request.getHeader("Wechatpay-Signature");
        String wechatpaySerial = request.getHeader("Wechatpay-Serial");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpaySignatureType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatpaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatpaySignature)
                .timestamp(wechatpayTimestamp)
                .body(body)
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            //业务处理
            businessProcess(transaction);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        } catch (Exception e) {
            log.info("接收回调业务处理异常", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }

    @Transactional
    public synchronized void businessProcess(Transaction transaction) {
        log.info("开始支付回调业务处理 transaction: {}", JSONObject.toJSONString(transaction, JSONWriter.Feature.PrettyFormat));
        //订单表维护支付状态
        String uuid = transaction.getOutTradeNo();
        String mergeOrderNo = redisTemplate.opsForValue().get(String.format(Constants.TransactionType.Pay.getValue(), uuid));
        String[] orderArr = mergeOrderNo.split("_");
        String classOrderNo = "";
        List<String> productOrderNoList = new ArrayList<String>();
        for (String orderNo : orderArr) {
            if (orderNo.startsWith(Constants.OrderNo_Prefix.C.name())) {
                classOrderNo = orderNo;
                classOrderService.processOrder(orderNo, transaction);
            } else if (orderNo.startsWith(Constants.OrderNo_Prefix.P.name())) {
            	productOrderNoList.add(orderNo);
                productOrderService.processOrder(orderNo, transaction);
            } else {
                log.info("未知订单: {}", orderNo);
            }
        }
        //生成流水
        ClassOrder classOrder = null;
        ProductOrder productOrder = null;
        Set<String> poIdSet = new HashSet<String>();
        if (StringUtils.isNotEmpty(classOrderNo)) {
            classOrder = classOrderService.selectClassOrderByOrderNo(classOrderNo);
        }
        if (!CollectionUtils.isEmpty(productOrderNoList)) {
        	for (String poNo : productOrderNoList) {
        		productOrder = productOrderService.selectProductOrderByOrderNo(poNo);
        		poIdSet.add(productOrder.getId().toString());

        		//支付成功后删除购物车相关信息
        		List<ShopCart> shopCartList = shopCartService.selectShopCartList(new ShopCart(productOrder.getWechatUserid(), productOrder.getProductId()));
        		for (ShopCart shopCart : shopCartList) {
					shopCartService.deleteShopCartById(shopCart.getId());
				}
            }
        }
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setOutTradeNo(mergeOrderNo);
        paymentInfo.setClassOrderId(classOrder == null ? null : classOrder.getId());
        paymentInfo.setProductOrderIds(String.join(",", poIdSet));
        Long wechatUserid = classOrder == null ? productOrder.getWechatUserid() : classOrder.getWechatUserid();
        paymentInfo.setWechatUserid(wechatUserid);
        paymentInfo.setWxpayTradeNo(transaction.getOutTradeNo());
        paymentInfo.setTotalAmount(new BigDecimal(transaction.getAmount().getTotal()).divide(new BigDecimal(100)));
        paymentInfo.setPaymentType("微信支付");
        //paymentInfo.setSubject(String.join("&", p));
        paymentInfo.setPaymentTime(DateUtils.getTime());
        paymentInfoService.insertPaymentInfo(paymentInfo);
    }

    @Override
    public int refund(ClassOrder classOrder) {
        // 初始化服务
        RefundService service = new RefundService.Builder().config(config).build();
        // ... 调用接口
        try {
            String uuid = classOrder.getWxpayTradeNo();
            redisTemplate.opsForValue().set(String.format(Constants.TransactionType.Refund.getValue(), uuid), classOrder.getOrderNo());
            CreateRequest request = new CreateRequest();
            // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
            // 调用接口
            request.setOutTradeNo(uuid);
            request.setOutRefundNo(Constants.OrderNo_Prefix.C.name() + System.currentTimeMillis());
            request.setReason(classOrder.getCancelReason());
            request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
            AmountReq amount = new AmountReq();
            amount.setRefund(classOrder.getPayAmount().multiply(new BigDecimal(100)).longValue());
            amount.setTotal(classOrder.getPayAmount().multiply(new BigDecimal(100)).longValue());
            amount.setCurrency("CNY");
            request.setAmount(amount);
            log.info("发起退款请求: {}", request);
            Refund refund = service.create(request);
            log.info("返回退款请求: {}", refund);
            classOrder.setRefundNo(request.getOutRefundNo());
            classOrderService.updateClassOrder(classOrder);
        } catch (HttpException e) { // 发送HTTP请求失败
            throw new RuntimeException(e.getMessage());
        } catch (ServiceException e) {
            // 服务返回状态小于200或大于等于300，例如500
            throw new RuntimeException(e.getErrorMessage());
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            throw new RuntimeException(e.getMessage());
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }
        return 1;
    }

    @Override
    public int refund(ProductOrder productOrder) {
        // 初始化服务
        RefundService service = new RefundService.Builder().config(config).build();
        // ... 调用接口
        try {
            String uuid = productOrder.getWxpayTradeNo();
            redisTemplate.opsForValue().set(String.format(Constants.TransactionType.Refund.getValue(), uuid), productOrder.getOrderNo());
            CreateRequest request = new CreateRequest();
            // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
            // 调用接口
            request.setOutTradeNo(uuid);
            request.setOutRefundNo(Constants.OrderNo_Prefix.C.name() + System.currentTimeMillis());
            request.setReason(productOrder.getCancelReason());
            request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
            AmountReq amount = new AmountReq();
            amount.setRefund(productOrder.getRealPayAmount().multiply(new BigDecimal(100)).longValue());
            amount.setTotal(productOrder.getRealPayAmount().multiply(new BigDecimal(100)).longValue());
            amount.setCurrency("CNY");
            request.setAmount(amount);
            log.info("发起退款请求: {}", request);
            Refund refund = service.create(request);
            log.info("返回退款请求: {}", refund);
            productOrder.setRefundNo(request.getOutRefundNo());
            productOrderService.updateProductOrder(productOrder);
        } catch (HttpException e) { // 发送HTTP请求失败
            throw new RuntimeException(e.getMessage());
        } catch (ServiceException e) {
            // 服务返回状态小于200或大于等于300，例如500
            throw new RuntimeException(e.getErrorMessage());
            // 调用e.getResponseBody()获取返回体打印日志或上报监控，更多方法见ServiceException定义
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            throw new RuntimeException(e.getMessage());
            // 调用e.getMessage()获取信息打印日志或上报监控，更多方法见MalformedMessageException定义
        }
        return 1;
    }


    @Override
    public ResponseEntity<Void> refundNotify(HttpServletRequest request, String body) {
        //效验签名
        String wechatpaySignature = request.getHeader("Wechatpay-Signature");
        String wechatpaySerial = request.getHeader("Wechatpay-Serial");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpaySignatureType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatpaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatpaySignature)
                .timestamp(wechatpayTimestamp)
                .body(body)
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            //log.info("body: {}", body);
            RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);
            //业务处理
            businessRefundProcess(refundNotification);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        } catch (Exception e) {
            log.info("接收回调业务处理异常", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }

    @Override
    public AjaxResult prePayByOrder(String orderNo, String type) throws Exception {
        String uuid = IdUtils.simpleUUID();
        redisTemplate.opsForValue().set(String.format(Constants.TransactionType.Pay.getValue(), uuid), orderNo);
        BigDecimal payAmount;
        Long wxUserId;
        if (StringUtils.equals(type, "class")) {
            ClassOrderVo classOrderVo = classOrderService.selectClassOrderByOrderNo(orderNo);
            payAmount = classOrderVo.getPayAmount();
            wxUserId = classOrderVo.getWechatUserid();
        } else if (StringUtils.equals(type, "product")) {
            ProductOrder productOrder = productOrderService.selectProductOrderByOrderNo(orderNo);
            payAmount = productOrder.getPayAmount();
            wxUserId = productOrder.getWechatUserid();
        } else {
            return AjaxResult.error("参数错误");
        }
        WechatUser wechatUser = wechatUserService.selectWechatUserById(wxUserId);
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(payAmount.multiply(new BigDecimal(100)).intValue());
        //amount.setTotal(1);
        request.setAmount(amount);
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMchId());
        request.setDescription("秀小吉订单");
        request.setNotifyUrl(wxPayConfig.getNotifyUrl());
        //request.setAttach(helperScrambleOrderIncome.getTitle());
        Payer payer = new Payer();
        payer.setOpenid(wechatUser.getOpenid());
        request.setPayer(payer);
        request.setOutTradeNo(uuid);
        request.setAttach(orderNo);
        log.info("获取预支付标识: {}", JSONObject.toJSONString(request, JSONWriter.Feature.PrettyFormat));
        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response;
        try {
            response = service.prepayWithRequestPayment(request);
        } catch (ServiceException e) {
            log.error("获取预支付标识失败", e);
            throw new Exception(e.getErrorMessage());
        }
        JSONObject result = new JSONObject();
        result.put("orderId", orderNo);
        result.put("data", response);
        return AjaxResult.success(result);
    }

    private void businessRefundProcess(RefundNotification refundNotification) {
        log.info("开始退款回调业务处理 refundNotification: {}", JSONObject.toJSONString(refundNotification, JSONWriter.Feature.PrettyFormat));
        String uuid = refundNotification.getOutTradeNo();
        String orderNo = redisTemplate.opsForValue().get(String.format(Constants.TransactionType.Refund.getValue(), uuid));
        processRefund(orderNo, refundNotification);
    }

    private synchronized void processRefund(String orderNo, RefundNotification refundNotification) {
        ClassOrder classOrder = null;
        ProductOrder productOrder = null;
        String reason = null;
        if (orderNo.startsWith(Constants.OrderNo_Prefix.C.name())) {
            classOrder = classOrderService.selectClassOrderByOrderNo(orderNo);
            reason = classOrder.getCancelReason();
            classOrderService.processRefund(orderNo, refundNotification);
        } else if (orderNo.startsWith(Constants.OrderNo_Prefix.P.name())) {
            productOrder = productOrderService.selectProductOrderByOrderNo(orderNo);
            productOrderService.processRefund(orderNo, refundNotification);
            reason = productOrder.getCancelReason();
        } else {
            log.info("未知订单: {}", orderNo);
            return;
        }
        //生成退款流水
        RefundInfo refundInfo = new RefundInfo();
        refundInfo.setOutRefundNo(refundNotification.getOutRefundNo());
        refundInfo.setClassOrderId(classOrder == null ? null : classOrder.getId());
        refundInfo.setProductOrderId(productOrder == null ? null : productOrder.getId());
        Long wechatUserid = classOrder == null ? productOrder.getWechatUserid() : classOrder.getWechatUserid();
        refundInfo.setWechatUserid(wechatUserid);
        refundInfo.setStatus(refundNotification.getRefundStatus().name());
        refundInfo.setAmount(new BigDecimal(refundNotification.getAmount().getTotal()).divide(new BigDecimal(100)));
        refundInfo.setUserReceivedAccount(refundNotification.getUserReceivedAccount());
        Channel channel = refundNotification.getChannel();
        if (Objects.nonNull(channel)) {
            refundInfo.setChannel(channel.name());
        }
        refundInfo.setRefundTime(DateUtils.getTime());
        refundInfo.setReason(reason);
        refundInfo.setCreateTime(new Date());
        refundInfo.setUpdateTime(new Date());
        refundInfoService.insertRefundInfo(refundInfo);
    }

}
